package com.hoas.api.core;

import com.hoas.api.core.intf.ApiListener;
import com.hoas.api.core.entity.BusinessUser;
import com.hoas.api.core.entity.UserAuth;
import com.hoas.api.core.entity.UserToken;
import com.hoas.api.core.entity.api.BeanBusinessApi;
import com.hoas.api.core.entity.api.BusinessApi;
import com.hoas.api.core.entity.api.HttpRequestBusinessApi;
import com.hoas.api.core.exception.UserAuthCreateException;
import com.hoas.api.core.intf.BusinessApiConfig;
import com.hoas.api.core.spring.SpringApplicationContext;
import com.hoas.api.encrypt.type.MD5;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;

/**
 * XML形式的配置支持
 * 
 * @author ZhangQingmu
 * @date 2016年10月23日
 * 
 */
public class XMLApiConfig implements BusinessApiConfig {

	public XMLApiConfig(String configFile) {
		this.configFile = configFile;
	}

	public XMLApiConfig() {
	}

	private static Logger logger = LoggerFactory.getLogger(XMLApiConfig.class);

	/**
	 * 默认配置文件路径
	 */
	private static final String DEFAULT_CONFIG_FILE = "/api-config.xml";

	/**
	 * API服务缓存
	 */
	private Map<String, BusinessApi> apisCache = new HashMap<>();
	/**
	 * 用户缓存
	 */
	private Map<String, BusinessUser> usersCache = new HashMap<>();
	/**
	 * 用户授权API缓存
	 */
	private Map<String, UserAuth> userAuthsCache = new HashMap<>();

	/**
	 * 用户令牌缓存
	 */
	private Map<String, UserToken> userTokenCache = new HashMap<>();

	/**
	 * 用户刷新令牌缓存
	 */
	private Map<String, String> userRefreshTokenCache = new HashMap<>();

	/**
	 * 用户操作令牌和刷新令牌关联
	 */
	private Map<String, String> userRefreshTokenAndAccessTokenRel = new HashMap<>();

	/**
	 * 接口调用前监听器
	 */
	private List<ApiListener> apiListeners = new ArrayList<>();


	/**
	 * Spring ApplicationContext 支持
	 */
	private SpringApplicationContext springApplicationContext;

	/**
	 * 配置文件
	 */
	private String configFile = DEFAULT_CONFIG_FILE;

	/**
	 * TOKEN保持时长（分）
	 */
	private long tokenKeepTime = 120;

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.hoas.datats.core.BusinessApiFactory#get(java.lang.String)
	 */
	@Override
	public BusinessApi getApi(String businessId) {
		return apisCache.get(businessId);
	}

	@Override
	public BusinessUser getUserByAccessToken(String accessToken) {

		UserToken userToken = userTokenCache.get(accessToken);
		if (userToken == null) {
			return null;
		}

		if (userToken.getExpireTime() == 0 || userToken.getExpireTime() >= new Date().getTime()) {
			return usersCache.get(userToken.getUser());
		}

		userTokenCache.remove(accessToken);

		String refreshToken = userRefreshTokenAndAccessTokenRel.get(accessToken);
		userRefreshTokenAndAccessTokenRel.remove(accessToken);
		userRefreshTokenCache.remove(refreshToken);

		return null;
	}

	@Override
	public boolean isAuthorized(BusinessUser user, BusinessApi api) {
		UserAuth auth = userAuthsCache.get(user.getUserName());
		if (auth == null) {
			return false;
		}

		if (auth.getApis() == null) {
			return false;
		}

		for (String apiBusinessId : auth.getApis()) {
			if (api.getBusinessId().equals(apiBusinessId)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public void initConfigs() throws Exception {
		SAXReader reader = new SAXReader();

		logger.debug("load api config file [%s]", configFile);

		try {
			InputStream in = XMLApiConfig.class.getResourceAsStream(configFile);
			Document doc = reader.read(in);
			Element root = doc.getRootElement();

			initApisConfig(root);
			initUsersConfig(root);
			initAuthsConfig(root);
			initApiListenersConfig(root);

		} catch (DocumentException e) {
			logger.error("配置文档解析异常", e);
		}

	}

	@SuppressWarnings("unchecked")
	protected void initApiListenersConfig(Element root) throws Exception {
		logger.debug("init user listeners config....");
		Element listeners = root.element("listeners");
		if (listeners == null) {
			return;
		}

		List<Element> listenerList = listeners.elements("listener");
		for (Element listener : listenerList) {
			String type = listener.attributeValue("type");
			String className = listener.attributeValue("class");
			String springBean = listener.attributeValue("springBean");

			try {
				ApiListener listenerObj = null;
				if (springBean == null) {
					Class<ApiListener> clazz = (Class<ApiListener>) Class.forName(className);
					listenerObj = clazz.newInstance();
				} else {
					if (springApplicationContext == null) {
						throw new NullPointerException("SpringApplicationContext对象未注入");
					}
					listenerObj = (ApiListener) springApplicationContext.getBean(springBean);
				}


				apiListeners.add(listenerObj);


				logger.debug("监听器初始化成功：{}", listenerObj.getClass());
			} catch (Exception e) {
				logger.error("监听器初始化失败", e);
			}

		}

	}

	@SuppressWarnings("unchecked")
	protected void initAuthsConfig(Element root) throws UserAuthCreateException {
		logger.debug("init user auths config....");
		Element auths = root.element("auths");
		if (auths == null) {
			return;
		}

		List<Element> authList = auths.elements("auth");
		for (Element auth : authList) {
			String user = auth.attributeValue("user");
			String apis = auth.attributeValue("apis");

			UserAuth userAuth = new UserAuth(user);

			if (apis != null && !"".equals(apis)) {
				String[] apiList = apis.split(",");
				if (apiList != null && apiList.length > 0) {
					List<String> authData = new ArrayList<>();
					for (String api : apiList) {
						authData.add(api);
					}
					userAuth.setApis(authData);
				}
			}

			userAuthsCache.put(user, userAuth);

		}

	}

	/**
	 * 初始化用户
	 * 
	 * @author ZhangQingmu
	 * @date 2016年10月20日
	 * @param root
	 */
	@SuppressWarnings("unchecked")
	protected void initUsersConfig(Element root) throws Exception {
		logger.debug("init users config....");
		Element users = root.element("users");
		if (users == null) {
			return;
		}

		List<Element> userList = users.elements("user");
		for (Element user : userList) {
			String username = user.attributeValue("username");
			String password = user.attributeValue("password");
			String encryptType = user.attributeValue("encryptType");
			String encryptKey = user.attributeValue("encryptKey");
			String needLogin = user.attributeValue("needLogin");

			BusinessUser businessUser = null;
			if ("false".equals(needLogin)) {
				businessUser = new BusinessUser(username, "00000000");
				businessUser.setNeedLogin(false);

				getToken(businessUser);

			} else {
				businessUser = new BusinessUser(username, password);
			}

			businessUser.setEncryptType(encryptType);
			businessUser.setEncryptKey(encryptKey);

			usersCache.put(username, businessUser);

		}

	}

	/**
	 * 初始化API
	 * 
	 * @author ZhangQingmu
	 * @date 2016年10月20日
	 * @param root
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	protected void initApisConfig(Element root) throws Exception {
		logger.debug("init apis config....");

		Element apis = root.element("apis");
		if (apis == null) {
			return;
		}

		List<Element> apiList = apis.elements("api");
		for (Element api : apiList) {
			String businessId = api.attributeValue("businessId");
			String name = api.attributeValue("name");
			String description = api.attributeValue("description");
			String type = api.attributeValue("type");

			BusinessApi apiObj = null;
			if ("http".equals(type)) {
				apiObj = createHttpRequestBusinessApi(businessId, api);
			} else {
				apiObj = createBeanBusinessApi(businessId, api);
			}

			apiObj.setDescription(description);
			apiObj.setName(name);

			apisCache.put(businessId, apiObj);

			logger.debug("init api[%s] success....", apiObj.getBusinessId());
		}

	}

	/**
	 * 创建处理HTTP请求方式的API
	 * 
	 * @author ZhangQingmu
	 * @date 2016年11月9日
	 * @param businessId
	 * @param api
	 * @return
	 */
	protected BusinessApi createHttpRequestBusinessApi(String businessId, Element api) throws Exception {
		String url = api.attributeValue("url");
		String method = api.attributeValue("method");

		HttpRequestBusinessApi apiObj = new HttpRequestBusinessApi(businessId);
		apiObj.setUrl(url);
		apiObj.setMethod(method == null || "".equals(method) ? "GET" : method.toUpperCase());

		return apiObj;
	}

	/**
	 * 创建Bean处理方式的API
	 * 
	 * @author ZhangQingmu
	 * @date 2016年11月9日
	 * @param businessId
	 * @param api
	 * @return
	 * @throws Exception
	 */
	protected BusinessApi createBeanBusinessApi(String businessId, Element api) throws Exception {
		String methodName = api.attributeValue("method");
		String className = api.attributeValue("class");
		String springBean = api.attributeValue("springBean");

		BeanBusinessApi apiObj = new BeanBusinessApi(businessId);
		if (springBean == null) {
			Class<?> clazz = Class.forName(className);
			apiObj.setBusinessObj(clazz.newInstance());
		} else {
			if (springApplicationContext == null) {
				throw new NullPointerException("SpringApplicationContext对象未注入");
			}
			apiObj.setBusinessObj(springApplicationContext.getBean(springBean));
		}

		Method method = apiObj.getBusinessObj().getClass().getMethod(methodName, Map.class);
		apiObj.setMethod(method);

		return apiObj;
	}

	@Override
	public BusinessUser getUserByUsername(String username) {
		return usersCache.get(username);
	}

	@Override
	public UserToken getToken(BusinessUser user) {
		String accessToken = user.getUserName();

		UserToken token = null;
		if (user.isNeedLogin()) {
			String salt = user.getUserName() + new Date().getTime();
			accessToken = MD5.sign(salt, UUID.randomUUID().toString(), "UTF-8");
			String refreshToken = MD5.sign(salt, UUID.randomUUID().toString(), "UTF-8");
			token = new UserToken(user.getUserName(), accessToken, refreshToken);
			token.setExpireTime(new Date().getTime() + tokenKeepTime * 6000);

			userRefreshTokenCache.put(refreshToken, accessToken);
			userRefreshTokenAndAccessTokenRel.put(accessToken, refreshToken);
		} else {
			token = new UserToken(user.getUserName(), accessToken, null);
			token.setExpireTime(0);
		}

		userTokenCache.put(accessToken, token);

		return token;

	}

	@Override
	public UserToken refreshToken(String refreshToken) {
		String accessToken = userRefreshTokenCache.get(refreshToken);
		if (accessToken == null || "".equals(accessToken)) {
			return null;
		}

		BusinessUser user = getUserByAccessToken(accessToken);

		if (user == null) {
			return null;
		}

		return getToken(user);

	}

	public Map<String, BusinessApi> getApisCache() {
		return apisCache;
	}

	public void setApisCache(Map<String, BusinessApi> apisCache) {
		this.apisCache = apisCache;
	}

	public Map<String, BusinessUser> getUsersCache() {
		return usersCache;
	}

	public void setUsersCache(Map<String, BusinessUser> usersCache) {
		this.usersCache = usersCache;
	}

	public Map<String, UserToken> getUserTokenCache() {
		return userTokenCache;
	}

	public void setUserTokenCache(Map<String, UserToken> userTokenCache) {
		this.userTokenCache = userTokenCache;
	}

	public Map<String, UserAuth> getUserAuthsCache() {
		return userAuthsCache;
	}

	public void setUserAuthsCache(Map<String, UserAuth> userAuthsCache) {
		this.userAuthsCache = userAuthsCache;
	}

	public SpringApplicationContext getSpringApplicationContext() {
		return springApplicationContext;
	}

	public void setSpringApplicationContext(SpringApplicationContext springApplicationContext) {
		this.springApplicationContext = springApplicationContext;
	}

	public String getConfigFile() {
		return configFile;
	}

	public void setConfigFile(String configFile) {
		this.configFile = configFile;
	}

	public void setTokenKeepTime(long tokenKeepTime) {
		this.tokenKeepTime = tokenKeepTime;
	}

	public long getTokenKeepTime() {
		return tokenKeepTime;
	}

	@Override
	public List<ApiListener> getApiListeners() {
		return apiListeners;
	}

	public void setApiListeners(List<ApiListener> apiListeners) {
		this.apiListeners = apiListeners;
	}

}
